package de.herberlin.server.httpd;

import java.io.*;
import java.net.*;
import java.util.logging.Logger;
import java.util.prefs.BackingStoreException;

import de.herberlin.server.common.*;
import de.herberlin.server.common.ConfigConstants;
import de.herberlin.server.common.Configuration;
import de.herberlin.server.common.event.ApplicationEvent;
import de.herberlin.server.common.event.EventDispatcher;
import de.herberlin.wwwutil.ProxyResponse;
import de.herberlin.wwwutil.RequestHeader;
import de.herberlin.wwwutil.httperror.*;

public class HttpThread implements Runnable {

    private static Logger logger = Logger.getLogger(HttpThread.class.getName());
    private Thread theThread;
    private final Socket client;

    /**	Constructor		*/
    public HttpThread(Socket socket) {
        client = socket;
        theThread = new Thread(this);
        theThread.start();
        logger.finer("Thread started for socket=" + socket);
    }

    public void run() {

        EventDispatcher.add(
            new ApplicationEvent(ApplicationEvent.CONNECTION_ESTABLISHED));


       
        HttpData data=new HttpData();
        data.port=client.getLocalPort();
        data.inetAddress=client.getInetAddress();

        try {

            // setup httpdata
            try {
                data.req=new RequestHeader(client.getInputStream());
                data.out = new BufferedOutputStream(client.getOutputStream());
            } catch (IOException e) {
                ConnectionLost ex = new ConnectionLost(e);
                logger.fine(ex + "");
                throw ex;
            }
            data.realPath = getMappedFilename(data.req.getPath());

            if (data.realPath.isDirectory()) {

                new DirectoryListHttpHandler(data).perform();

            } else /* Not a directory but a file*/ {

                String fileType = data.req.getFileType();
                data.mimeType =
                    Configuration.getMimeTypes().get(
                        fileType,
                        "application/octet-stream");
                logger.fine(
                    "FileType=" + fileType + " MimeType=" + data.mimeType);

                if (fileType.indexOf("shtm") == 0) {

                    new SsiHandler(data).perform();

                } else if (
                    Configuration.getExtensions().get(fileType, null)
                        != null) {

                    new CgiRequestHandler(data).perform();

                } else {

                    // print ordinary file
                    new BasicHttpHandler(data).perform();
                }
            }

            // closing
            try {
                data.out.close();
            } catch (IOException e) {
                logger.fine(e + "");
            }
            logger.fine("Successfull:" + data.req.getPath());
        } catch (ConnectionLost e) {
            // do nothing
        } catch (InternalServerError_500 e) {
            writeHttpError(e, data);
        } catch (MovedTemporarily_302 e) {
            writeHttpError(e, data);
        } catch (NotFound_404 e) {
            writeHttpError(e, data);
        } catch (HttpError e) {
            writeHttpError(e, data);
        } catch (Throwable t) {
            writeHttpError(new InternalServerError_500(t),data);
        } finally {
            try {
                client.close();
            } catch (Throwable t) {
                logger.fine(t + "");
            }
            EventDispatcher.add(data.asEvent());
            EventDispatcher.add(
                new ApplicationEvent(ApplicationEvent.CONNECTION_CLOSED));
        }
    }
    private void writeHttpError(HttpError error, HttpData data) {
        logger.throwing(getClass().getName(),"writeHttpError",error);
        if (data.out != null) {

            try {
                error.write(data.out);
                data.out.flush();
                
                // event FileData
                ByteArrayOutputStream out=new ByteArrayOutputStream();
                error.write(out);
                byte[] errorHtml=out.toByteArray();
                data.fileData.setContentLength(errorHtml.length);
                data.fileData.setContentType("text/html");
                data.fileData.setData(errorHtml);
                
                // Event response
                data.resp=new ProxyResponse(new ByteArrayInputStream(errorHtml));
                
            } catch (Exception e) {
                e.printStackTrace();
                logger.severe(e + "");
            }
        } else {
            logger.severe("OutputStream in null.");
        }
    }
    
    public static File getMappedFilename(String reqFile, URL referringUrl) throws NotFound_404, InternalServerError_500 {
        URL newUrl=null;
        try {
            newUrl=new URL(referringUrl,reqFile);
        } catch (MalformedURLException e) {
            logger.throwing("getMappedFilename",reqFile,e);
            throw new InternalServerError_500(e);
        }
        logger.fine("getMappedFilename url="+newUrl);
        return getMappedFilename(newUrl.getPath());
    }
    
    /** 
     * maps url to filesystem 
     * */
    public static File getMappedFilename(String reqFile)
        throws InternalServerError_500, NotFound_404 {
        if (!reqFile.startsWith("/")) {
        	reqFile="/"+reqFile;
        }
        String path = 
            Configuration.getPrefs().get(
                ConfigConstants.HTTP_DOCROOT,
                System.getProperty("user.dir"))
                + reqFile;
        String[] keys = null;

        try {
            keys = Configuration.getAliasDirectories().keys();
        } catch (BackingStoreException e) {
            logger.throwing(HttpThread.class.getName(), "getMappedFilename", e);
            throw new InternalServerError_500(e);
        }

        for (int i = 0; i < keys.length; i++) {
            if (reqFile.indexOf(keys[i]) == 0) {
                path =
                    (String) Configuration.getAliasDirectories().get(
                        keys[i],
                        null)
                        + reqFile.substring(keys[i].length());
            }
        }

        File f = new File(path.replace('/', File.separatorChar));
        logger.fine("MappedFilename for: " + reqFile + " is: " + f);

        if (f.exists()) {
            return f;
        } else {
            NotFound_404 ex = new NotFound_404("File not Found: " + reqFile);
            logger.throwing(HttpThread.class.getName(), "getMappedFilename", ex);
            throw ex;
        }
    }

}